
#ifndef __LIBERTY_LIBRARY_SYNCEDQUEUE_H__
#define __LIBERTY_LIBRARY_SYNCEDQUEUE_H__

#include <Liberty3D/Liberty3D.h>
#include <Liberty3D/Library/Task.h>
#include <Liberty3D/Library/TaskScheduler.h>

#include <boost/thread/shared_mutex.hpp>
#include <boost/thread/locks.hpp>
#include <list>

namespace Liberty { namespace Library {
    template <typename Type, typename Processor, typename Queuer, typename ThisMutexProvider, typename LockObject>
    class SyncedProcessQueue {
    public:
        typedef typename ThisMutexProvider::Mutex          Mutex;
        typedef typename ThisMutexProvider::SharedLock     SharedLock;
        typedef typename ThisMutexProvider::UpgradableLock UpgradableLock;
        typedef typename ThisMutexProvider::UpgradedLock   UpgradedLock;
        typedef typename ThisMutexProvider::UniqueLock     UniqueLock;

    protected:
        std::list<Type>       _list;
        Mutex                 _mutex;
        Processor             _processor;
        Queuer                _queuer;
        volatile bool         _task;

    public:
        SyncedProcessQueue(const Processor& processor, const Queuer& queuer) : _task(false), _processor(processor), _queuer(queuer) {
        }

        void queue(const Type& object) {
            UpgradableLock guard(_mutex);
            
            auto e(_list.end());
            auto i(std::find(_list.begin(), e, object));

            if (i != e) {
                return;
            }

            UpgradedLock guardEx(guard);

            _list.push_back(object);

            if (!_task) {
                _queuer();
                _task = true;
            }
        }

        void process() {
            Type o;

            while (true) {
                bool valid = false;

                {
                    SharedLock guard(_mutex);

                    if (_list.size()) {
                        o     = *_list.begin();
                        valid = true;
                    }
                }

                std::unique_ptr<LockObject> lock;

                if (valid) {
                    _processor(o, lock);
                }

                UniqueLock guardEx(_mutex);

                if (valid) {
                    _list.erase(_list.begin());
                }

                if (_list.size() == 0) {
                    _task = false;
                    break;
                }
            }
        }

        inline Processor& processor() { return _processor; }
        inline Queuer&    queuer()    { return _queuer; }
    };
}}

#endif
